<?php

class Catalog extends A_Content {
	
	private $_avalible_parametrs 
		= array(
			'category' => array(
				'cover' => array('cover_id','cover','Изображение'),
				'bannerlist' => array('bannerlist_id','bannerlist','Банеры')
			),
		
			'item' => array(
				'price' => array('price',null,'Цена'),
				'new_f' => array('new_f',null,'Новинка'),
				'sales_leader_f' => array('sales_leader_f',null,'Лидер продаж'),
				'description' => array('html_id','html','Описание'),
				'short' => array('short_id','html','Короткое описание'),
				'cover' => array('cover_id','cover','Изображение'),
				'gallery' => array('gallery_id','gallery','Изображения'),
				'standart_parameters' => array('catalog_parameters',null,'Стандартные параметры'),
				'parameters' => array('parameters',null,'Опциональные параметры'),
				'rating' => array('rating_id','rating','Рэйтинг'),
				'comments' => array('comments_id','comments','Комментарии')
			
			)
		); 

		
	public function __get($param) {
		if ($param=='avalible_parametrs') {
			return $this->_avalible_parametrs;
		} else {
			throw $this->_top->Utils->PushError('Trying to access to undefined Catalog parametr: '.$param,'Системная ошибка. Обратитесь к разработчикам');
		}
	}
	
		
	/**
	 * Returns an instance of SingleCatalog class with gived Id
	 *
	 * @param Integer $id
	 * @return SingleCatalog
	 */
	public function getById($id) {
		if (!isset($this->_cashe_obj[$id])) {
			$this->_cashe_obj[$id] = new SingleCatalog($this->_top,$this,$id);
		}
		
		return $this->_cashe_obj[$id];
	}
	
	public function add()
	{
		$this->_top->_sql->exeDummy("INSERT INTO {$this->_top->_sql->tab_pref}catalog(add_date,last_mod) VALUES(NOW(),NOW());");
		$id = $this->_top->_sql->last_insert_id();
		return $this->getById($id);
	}
}

class SingleCatalog extends A_SingleContent {
	
	private $_category_cashe = array();
	private $_item_cashe = array();
	
	public $config;
	
	protected function loader() {
		unset($this->config);
		
		$get_catalog = $this->_top->_sql->exe("SELECT * FROM {$this->_top->_sql->tab_pref}catalog WHERE catalog_id={$this->_content_id}");
        if (count($get_catalog)==0) {
             throw $this->_top->Utils->PushError("Catalog with Id: {$this->_content_id} not found","Системная ошибка. Обратитесь к администратору.");
        } else {
             $this->_data = $get_catalog[0];
		     return true;
        }
    }
	
	public function __get($param)
	{
		if ($param=='config') {
			$options = array(
				'category' => array(),
				'item' => array()); 
			
			if (strlen(trim($this->category_options))>0) {
				$used_category_parametrs = explode(',',$this->category_options);
				for ($c=0;$c<count($used_category_parametrs);$c++) {
					$options['category'][$used_category_parametrs[$c]] = $this->_parent->avalible_parametrs['category'][$used_category_parametrs[$c]];
				}
			}
			
			if (strlen(trim($this->item_options))>0) {
				$used_item_parametrs = explode(',',$this->item_options);
				for ($i=0;$i<count($used_item_parametrs);$i++) {
					$options['item'][$used_item_parametrs[$i]] = $this->_parent->avalible_parametrs['item'][$used_item_parametrs[$i]];
				}
			}
			
			return $options;
		} elseif ($param=='parameters') {
			Zend_Json::$useBuiltinEncoderDecoder = true;
	    	return Zend_Json::decode($this->_data[$param]);
		} elseif (isset($this->_data[$param])) {
			return $this->_data[$param];
		} else {
			return $this->_drive_class->$param;
		}
	}
	
	public function __set($param,$value) { 
		if (isset($this->_data[$param])) {
			if ($param == 'last_mod') {
				$this->_top->_sql->exeDummy("UPDATE {$this->_top->_sql->tab_pref}catalog SET last_mod=now() WHERE catalog_id={$this->_content_id}");
				$this->_data[$param] = $value;
				return true;
			}
		} else {
			$this->_drive_class->$param = $value;
		}
		return false;
	}
	
	
	protected function deleter() {
		$this->_top->_sql->exeDummy("DELETE {$this->_top->_sql->tab_pref}catalog WHERE catalog_id={$this->_content_id}");
	}
	
	/**
	 * REturns SingleCategory pointer
	 *
	 * @param Integer $id
	 * @return CatalogSingleCategory
	 */
	public function getCategoryById($id) {
		if (!isset($this->_category_cashe[$id])) {
			$this->_category_cashe[$id] = new CatalogSingleCategory($this->_top,$this,$id);
		}
		return $this->_category_cashe[$id];
	}
	
	/**
	 * Alias for SingleCatalog::getItemById
	 *
	 * @param unknown_type $id
	 * @return CatalogSingleItem
	 */
	public function getById($id) {
		return $this->getItemById($id);
	}
	
	
	/**
	 * Returns SingleItem pointer
	 * 
	 * @param Integer $id
	 * @return CatalogSingleItem
	 */
	public function getItemById($id) {
		if (!isset($this->_item_cashe[$id])) {
			$this->_item_cashe[$id] = new CatalogSingleItem($this->_top,$this,$id);
		}
		return $this->_item_cashe[$id];
	}
	
	/**
	 * ...
	 *
	 * @return CatalogSingleCategory
	 */
	public function CategoryList() {
		return $this->getCategoryById(0);
	}
	
	/**
	 * Gets all items
	 *
	 * @return CatalogItemSearch
	 */
	public function ItemList() {
		$item_search = new CatalogItemSearch($this->_top,$this);
		$item_search->Make();
		return $item_search;
	}
	
	/**
	 * Adds new item to catalog
	 *
	 * @return CatalogSingleItem
	 */
	public function addItem() {
		$this->_top->_sql->exeDummy("INSERT INTO `{$this->_top->_sql->tab_pref}catalog_item` (`catalog_id`,`add_date`) 
			VALUES('{$this->_content_id}', NOW())");
		return $this->getItemById($this->_top->_sql->last_insert_id());
	}
	
	/**
	 * Lanches filter
	 *
	 * @return CatalogItemSearch
	 */
	public function filter() {
		return new CatalogItemSearch($this->_top,$this);
	}
	
}

class CatalogSingleCategory extends A_SingleContent {

	/**
	 * Enter description here...
	 *
	 * @var CatalogItemSearch
	 */
	public $items;
	
	private $_sub_categories = false;
	
	/**
	 * Stores pointer to parent SingleCatalog class
	 *
	 * @var SingleCatalog
	 */
	private $_catalog;
	
	private $_extra_data_map;
	
	public $config;
	
	protected function loader() {
		unset($this->config);
		unset($this->items);
		
		$this->_catalog = $this->_parent;
		
		$this->_extra_data_map = $this->_parent->config['category'];
		
		if ($this->_content_id!=0) {
			$get_data = $this->_top->_sql->exe("SELECT * FROM `{$this->_top->_sql->tab_pref}catalog_category` 
				WHERE `category_id`={$this->_content_id}");
			if (count($get_data)==0) {
				throw $this->_top->Utils->PushError("Category not found Category_id: {$this->_content_id}","Системная ошибка. Обратитесь к разработчикам");
			} else {
				$this->_data = $get_data[0];
			}
		}
	}
	
	public function __get($param) {
		if ($param=='config') {
			Zend_Json::$useBuiltinEncoderDecoder = true;
			return Zend_Json::decode($this->options);
		} elseif ($param=='items') {
			return $this->_catalog->filter()->appendCategoryCondition($this)->Make();
		} elseif (isset($this->_data[$param])) {
			return $this->_data[$param];
		} elseif (isset($this->_extra_data_map[$param])) {
			list($field_name,$content_name) = $this->_extra_data_map[$param];  
			if ($content_name===NULL) {
				return $this->__get($field_name);
			} else {
				try {
					return $this->_top->Content($content_name)->getById($this->__get($field_name));
				} catch (Exception $e) {
					$new_content = $this->_top->Content($content_name)->add();
					$this->db_updater($field_name,$new_content->_content_id);
					return $new_content;
				}
			}
		} else {
			throw $this->_top->Utils->PushError('Geting Undefinded CatalogSingleCategory object property. Property: '.$param,'Системная ошибка. Обратитесь к разработчикам');
		}
		
	}
	
	public function __set($param,$value) {
		if ($param=="name") {
			$this->db_updater($param,$value);
		} elseif ($param=="config") {
			// TODO на перспективу сделать управление стандартными опциями для каталога
		} else {
			throw $this->_top->Utils->PushError('Seting Undefinded CatalogSingleCategory object property','Системная ошибка. Обратитесь к разработчикам');
		}
		
	}
	
	private function db_updater($param,$value) {
		$value = $this->_top->Utils->prepareValueForSQL($value);
		$this->_top->_sql->exeDummy("UPDATE `{$this->_top->_sql->tab_pref}catalog_category` 
			SET `$param`='$value' WHERE category_id={$this->_content_id}");
	}
	
	
	private function cashe_categories() {
		$get_list = $this->_top->_sql->exe("SELECT `category_id` FROM `{$this->_top->_sql->tab_pref}catalog_category` 
			WHERE `root_category_id`={$this->_content_id} AND `catalog_id`='{$this->_parent->_content_id}'");
		
		$this->_sub_categories = array();
		for ($l=0;$l<count($get_list);$l++) {
			array_push($this->_sub_categories,$get_list[$l]['category_id']);
		}
	}
	
	public function length() {
		if (!$this->_sub_categories) {
			$this->cashe_categories();
		}
		return count($this->_sub_categories);
	}
	
	
	/**
	 * Returns pointer to SingleCategory
	 *
	 * @param Integer $idx
	 * @return CatalogSingleCategory
	 */
	public function item($idx) {
		return $this->_catalog->getCategoryById($this->_sub_categories[$idx]);
	}
	
	/**
	 * Returns pointer to parent category
	 *
	 * @return CatalogSingleCategory
	 */
	public function parentCategory() {
		return $this->_catalog->getCategoryById($this->root_category_id);
	}
	
	
	/**
	 * Adds new SingleCategory returns pointer to it 
	 *
	 * @return CatalogSingleCategory
	 */
	public function add() {
		$this->_top->_sql->exeDummy("INSERT INTO `{$this->_top->_sql->tab_pref}catalog_category`(`root_category_id`,`catalog_id`)
			VALUES({$this->_content_id},{$this->_catalog->_content_id})");
		$this->cashe_categories();
		return $this->_catalog->getCategoryById($this->_top->_sql->last_insert_id());
	}
	

	protected function deleter() {
		# deleting all sub categiries
		for ($i=0;$i<$this->length();$i++) {
			$this->item($i)->delete();
		}
		
		# deleting links from this category to items
		for ($i=0;$i<$this->items->length();$i++) {
			$this->items->item($i)->unlink($this);
		}
		
		# Если возвращать нужно только то, что указано в настройках как 
    	# используемые модули, то удалять нужно пытаться вообще все что привязано
    	$catalog_avalible_parametrs = $this->_top->Catalog->avalible_parametrs['category'];
    	$param_names = array_keys($catalog_avalible_parametrs);
    	for ($p=0;$p<count($param_names);$p++) {
    		list($field_name,$content_name) = $catalog_avalible_parametrs[$param_names[$p]];
    		if ($content_name!==null) {
	    		try {
		    		$content_object = $this->_top->Content($content_name)->getById($this->__get($field_name));
		    		$content_object->delete();
	    		} catch (Exception $e) { }
    		}
    	}
    	
		$this->_top->_sql->exeDummy("DELETE FROM {$this->_top->_sql->tab_pref}catalog_category WHERE category_id={$this->_content_id}");
	}
	
}

class CatalogItemSearch {
	
	/**
	 * Returns poiter to root class
	 *
	 * @var MF_Main
	 */
	private $_top;
	/**
	 * Returns pointer to parent SingleCagalog class
	 *
	 * @var SingleCatalog
	 */
	private $_catalog;
	
	private $_order = '';
	private $_conditions =  array ('tables' => array(),'conditions' => array());
	private $_limits = null;
	private $_results = false;
	private $_total_amount = null;
	private $_sort = null;
	
	public function __construct($top,$catalog) {
		$this->_top = $top;
		$this->_catalog = $catalog;
	}
	
	public function __set($param,$value) {
		if ($param=="category") {
			$this->appendCategoryCondition($value);
		} else {
			array_push($this->_conditions['conditions']," AND `i`.`$param`='$value'");
		}
	}
	
	/**
	 * Adds filtering by category
	 *
	 * @param Integer|CatalogSingleCategory $category
	 * @param boolean $append_child optional
	 * @return CatalogItemSearch
	 */
	public function appendCategoryCondition($category,$append_child = false) {
		if (gettype($category)=='object') {
			if (get_class($category)!="CatalogSingleCategory") {
				throw $this->_top->Utils->PushError('Undefined $category param type while appending filter category condition', 'Системная ошибка. Обратитесь к разработикам');
			}
		} else {
			$category = $this->_catalog->getCategoryById($category);
		}
		
		
		$category_list = array($category->_content_id);
		if ($append_child==TRUE) {
			$category_list = array_merge($category_list,$this->childCategoriesDigger($category));
		}
		
		array_push($this->_conditions['tables'], ", `{$this->_top->_sql->tab_pref}catalog_item2category` c");
		array_push($this->_conditions['conditions'],"AND `i`.item_id=`c`.`item_id` AND `c`.category_id IN (".implode(",",$category_list).")");

		return $this;
	}
	
	//добавляем связь с таблицей рейтинга
	public function appendItemRating($downwise = true){
		array_push($this->_conditions['tables'], ", `{$this->_top->_sql->tab_pref}rating` r");
		array_push($this->_conditions['conditions'],"AND `i`.rating_id=`r`.`rating_id`");
		
		$this->_sortBy('r.score', $downwise);
		
		return $this;
	}

	/**
	 * Digg for the child cetegories and returns them
	 *
	 * @param CatalogSingleCategory $category
	 * @return array
	 */
	private function childCategoriesDigger($category) {
		$category_list = array();
		
		for ($c=0;$c<$category->length();$c++) {
			array_push($category_list,$category->item($c)->_content_id);
			$category_list = array_merge($category_list,$this->childCategoriesDigger($category->item($c)));
		}
		
		return $category_list;
	}
	
	
	/**
	 * Makes searching only by new items
	 *
	 * @return CatalogItemSearch
	 */
	public function appendOnlyNewCondition() {
		array_push($this->_conditions['conditions'],"AND `i`.`new_f`=1");
		
		return $this;
	}
	
	public function appendOnlySalefLeaderCondition() {
		array_push($this->_conditions['conditions'],"AND `i`.`sales_leader_f`=1");
		
		return $this;
	}
	
	
	/**
	 * Makes searching by good name mask
	 *
	 * @param String $mask
	 * @return CatalogItemSearch
	 */
	public function appendNameMaskCondition($mask) {
		$mask = $this->_top->Utils->prepareValueForSQL($mask);
		array_push($this->_conditions['conditions'],"AND `i`.`name` LIKE '$mask'");
		return $this;
	}
	
	
	public function setLimits($from_row,$amount) {
		// TODO - need to make validation here
		$this->_limits = array($from_row,$amount);
	}
	
	
	/**
	 * Sets sorting by add date
	 *
	 * @param string $where - asc|desc
	 * @return CatalogItemSearch
	 */
	public function setSortByDate($where = '') {
		$where = trim(strtolower($where));
		if ($where!='asc' && $where!='desc') {
			$where = '';
		}
		$this->_order = ' ORDER BY add_date '.$where;
		return $this;
	}
	
	/**
	 * Sets sorting by price
	 *
	 * @param string $where - asc|desc
	 * @return CatalogItemSearch
	 */
	public function sortByPrice($where = '') {
		$where = trim(strtolower($where));
		if ($where!='asc' && $where!='desc') {
			$where = '';
		}
		$this->_order = ' ORDER BY price '.$where;
		return $this;
	}
	
	
	/**
	 * Sets sorting by name
	 *
	 * @param bool $downwise - points how to make sorting
	 * @return CatalogItemSearch
	 */
	public function sortByName($downwise = false) {
		$this->_sortBy('i.name',$downwise);
		return $this;
	}
	
	
	private function _sortBy($condition, $downwise) {
		$this->_order = ' order by '.$condition.' '. (!$downwise ? 'asc' : 'desc');
	}
	
	
	/**
	 * Starts searching
	 *
	 * @return CatalogItemSearch
	 */
	public function Make() {
		$sql = "FROM `{$this->_top->_sql->tab_pref}catalog_item` i ".(implode(' ',$this->_conditions['tables']))."
			WHERE i.catalog_id={$this->_catalog->_content_id}  ".(implode(' ',$this->_conditions['conditions']));
		
		$cnt = $this->_top->_sql->exe("SELECT count(*) as c $sql ");
		$this->_total_amount = $cnt[0]['c'];
		
		$res = $this->_top->_sql->exe("SELECT i.item_id $sql ".$this->_order
			.($this->_limits!==null ? " LIMIT {$this->_limits[0]},{$this->_limits[1]}" : ''));
		
		$this->_results = array();
		for ($r=0;$r<count($res);$r++) {
			array_push($this->_results,$res[$r]['item_id']);
		}
		
		return $this;
	}


	public function total_length() {
		return $this->_total_amount;
	}


	public function length() {
		if (!$this->_results) {
			$this->Make();
		}
		return count($this->_results);
	}


	/**
	 * Enter description here...
	 * 
	 * @param Integer $idx
	 * @return CatalogSingleItem
	 */
	public function item($idx) {
		if (!$this->_results) {
			$this->Make();
		}
		return $this->_catalog->getItemById($this->_results[$idx]);
	}
	
	
}


	

class CatalogSingleItem extends A_SingleContent {
	
	private $_extra_data_map;
	
	private $_category_list = array();
	
	protected function loader() {
		$get_catalog = $this->_top->_sql->exe("SELECT * FROM `{$this->_top->_sql->tab_pref}catalog_item` WHERE item_id={$this->_content_id}");
        if (count($get_catalog)==0) {
        	throw $this->_top->Utils->PushError("Catalog Item Id: {$this->_content_id} not found","Системная ошибка. Обратитесь к администратору.");
        } else {
            $this->_data = $get_catalog[0];
            
	        $this->_extra_data_map = $this->_parent->config['item'];
			
			$get_categories = $this->_top->_sql->exe("SELECT `category_id` 
				FROM `{$this->_top->_sql->tab_pref}catalog_item2category` 
				WHERE `item_id`={$this->_content_id}");
			for ($c=0;$c<count($get_categories);$c++) {
				array_push($this->_category_list,$get_categories[$c]['category_id']);
			}
			
		    return true;
        }
    }
	
    
    public function __get($param) {
    	if( $param=='catalog_parameters' || $param=="category_parameters" || $param=='parameters') {
    		Zend_Json::$useBuiltinEncoderDecoder = true;
		if( $this->_data[$param] != NULL )
	    	return Zend_Json::decode($this->_data[$param]);
		else
			return Zend_Json::decode('[]');
    	}/* elseif($param == 'rating' ){ //возвращаем объект рейтинга
            return MF_Main::getInstance()->Rating->getById($this->item_id);
    	}*/ elseif (isset($this->_data[$param])) {
			return $this->_data[$param];
		} elseif (isset($this->_extra_data_map[$param])) {
			list($field_name,$content_name) = $this->_extra_data_map[$param];  
			if ($content_name===NULL) {
				return $this->__get($field_name);
			} else {
				try {
					return $this->_top->Content($content_name)->getById($this->__get($field_name));
				} catch (Exception $e) {
					$new_content = $this->_top->Content($content_name)->add();
					$this->db_updater($field_name,$new_content->_content_id);
					return $new_content;
				}
			}
		} else {
			throw $this->_top->Utils->PushError('Geting Undefinded CatalogItem object property. Property: '.$param,'Системная ошибка. Обратитесь к разработчикам');
		}
    }
    
    public function __set($param,$value) {
    	if ($param=="item_id" || !isset($this->_data[$param])) { // TODO запретить изменение полей к которым идет привязка других объектов данных
    		throw $this->_top->Utils->PushError('Seting Undefinded CatalogItem object property','Системная ошибка. Обратитесь к разработчикам');
    	} else {
    		if ($param=="parameters" || $param=="category_parameters" || $param=="catalog_parameters") {
	    		if (gettype($value)=="object" || gettype($value)=="array") {
	    			Zend_Json::$useBuiltinEncoderDecoder = true;
	    			$value = Zend_Json::encode($value);
	    		}
    		}
    		
    		$this->db_updater($param,$value);
    	}
    }
    
    private function db_updater($param,$value) {
    	$value = $this->_top->Utils->prepareValueForSQL($value);
    		$this->_top->_sql->exeDummy("UPDATE `{$this->_top->_sql->tab_pref}catalog_item`
    			SET `$param`='$value' WHERE item_id={$this->_content_id}");
    }
    
    
    protected function deleter() {
    	# Если возвращать нужно только то, что указано в настройках как 
    	# используемые модули, то удалять нужно пытаться вообще все что привязано
    	$catalog_avalible_parametrs = $this->_top->Catalog->avalible_parametrs['item'];
    	$param_names = array_keys($catalog_avalible_parametrs);
    	for ($p=0;$p<count($param_names);$p++) {
    		list($field_name,$content_name) = $catalog_avalible_parametrs[$param_names[$p]];
    		if ($content_name!==null) {
	    		try {
		    		$content_object = $this->_top->Content($content_name)->getById($this->__get($field_name));
		    		$content_object->delete();
	    		} catch (Exception $e) { }
    		}
    	}
    	
    	$this->_top->_sql->exeDummy("DELETE FROM `{$this->_top->_sql->tab_pref}catalog_item2category` 
    		WHERE item_id={$this->_content_id}");
    	$this->_top->_sql->exeDummy("DELETE FROM `{$this->_top->_sql->tab_pref}catalog_item` 
    		WHERE item_id={$this->_content_id}");
    }
    
	
	/**
	 * Links item to category
	 *
	 * @param Integer|CatalogSingleCategory $category
	 * @return CatalogSingleItem
	 */
	public function link($category) {
		if (gettype($category)=='object') {
			if (get_class($category)=='CatalogSingleCategory') {
				$category = $category->_content_id;
			} else {
				throw $this->_top->Utils->PushError('Undefined gived varible type','Системная ошибка. Обратитесь к разработчикам');
			}
			
		}
		
		$this->_top->_sql->exeDummy("INSERT INTO `{$this->_top->_sql->tab_pref}catalog_item2category` (`item_id`,`category_id`)
			VALUES ('{$this->_content_id}','{$category}')");
		
		$this->loader();
		return $this;
	}
	
	
	/**
	 * Unlinks item from category
	 *
	 * @param Integer|CatalogSingleCategory $category
	 * @return CatalogSingleItem
	 */
	public function unlink($category) {
		if (gettype($category)=='object') {
			if (get_class($category)=='CatalogSingleCategory') {
				$category = $category->_content_id;
			} else {
				throw $this->_top->Utils->PushError('Undefined gived varible type','Системная ошибка. Обратитесь к разработчикам');
			}
			
		}
		
		$this->_top->_sql->exeDummy("DELETE FROM `{$this->_top->_sql->tab_pref}catalog_item2category` 
			WHERE `item_id`='{$this->_content_id}' AND `category_id`='{$category}'");
			
		$this->loader();
	}
	
	public function category_length() {
		return count($this->_category_list);
	}
	
	/**
	 * Returns pointer on linked CatalogSingleCategory object
	 *
	 * @param Integer $idx
	 * @return CatalogSingleCategory
	 */
	public function category_item($idx) {
		return $this->_parent->getCategoryById($this->_category_list[$idx]);
	}
	
}


?>
